﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using Jay.Sc2.Bof.Lib.Action;
using Jay.Sc2.Bof.Lib.State;

namespace Jay.Sc2.Bof.Windows
{
	public partial class ActionListForm : Form, IComparer<IAction>
	{

		private ActionList m_ActionList = new ActionList(Program.CurrentRace);

		public ActionListForm(IActionList actionList)
		{
			InitializeComponent();
			FillTerranActions();
			if (actionList != null)
				m_ActionList.AddRange(actionList);
			Run();
		}

		public void FillTerranActions()
		{
			List<IAction> actions = new List<IAction>(Program.CurrentRace.Actions);
			actions.Sort(this);
			cbAction.DataSource = actions;
		}

		public int Compare(IAction x, IAction y)
		{
			return x.ToString().CompareTo(y.ToString());
		}

		private void RefreshActionList()
		{
			lvActionList.BeginUpdate();
			lvActionList.Items.Clear();
			int count = 0;
			foreach (IAction action in m_ActionList)
			{
				count++;
				ListViewItem item = new ListViewItem(new string[]{
					count.ToString(),
					action.ToString()
				});
				lvActionList.Items.Add(item);
			}
			lvActionList.EndUpdate();
		}

		private void bAddAction_Click(object sender, EventArgs e)
		{
			m_ActionList.AddAction(cbAction.SelectedItem as IAction);
			Run();
		}

		private void bGo_Click(object sender, EventArgs e)
		{
			Run();
		}
		private void Run() 
		{
			IStateProxy proxy;
			int currentGoal = -1;
			IState initial = Program.CurrentRace.InitialState;
			BuildOrder bo = new BuildOrder(initial.Race, initial, null, new ActionList(m_ActionList));
			lvDetails.Items.Clear();
			int count = 0;
			int lastCount = 0;
			int lastTime = -1;
			for (int c = 0; c < m_ActionList.Count; c++)
			{
				IAction action = m_ActionList[c];
				if (action.IsInvalid(bo))
				{
					m_ActionList.RemoveAt(c);
					c--;
				}
				else
				{
					while (!action.CanExecute(bo))
						GenerationForm.IncrementTime(bo, ref count, ref lastCount, ref lastTime, lvDetails, ref currentGoal);
					action.Execute(bo);
					GenerationForm.AddListViewItemLine(bo, action, ref count, ref lastCount, ref lastTime, lvDetails);
				}
			}
			int lastAction = bo.LastActionTime;
			while (bo.State.Time < lastAction)
				GenerationForm.IncrementTime(bo, ref count, ref lastCount, ref lastTime, lvDetails, ref currentGoal);
			proxy = Program.GetProxy(bo.State);
			proxy.ShowOnlyIfPresent = true;
			pgState.SelectedObject = proxy;
			RefreshActionList();
		}

		private void insertToolStripMenuItem_Click(object sender, EventArgs e)
		{
			if (lvActionList.SelectedItems.Count > 0)
			{
				m_ActionList.InsertAction(lvActionList.SelectedIndices[0], cbAction.SelectedItem as IAction);
				Run();
			}
		}

		private void deleteToolStripMenuItem_Click(object sender, EventArgs e)
		{
			if (lvActionList.SelectedItems.Count > 0)
			{
				for (int c = lvActionList.SelectedItems.Count - 1; c >= 0; c--)
					m_ActionList.RemoveAt(lvActionList.SelectedIndices[c]);
				m_ActionList.AddMissingRequirements();
				Run();
			}
		}

		private void clearToolStripMenuItem_Click(object sender, EventArgs e)
		{
			m_ActionList.Clear();
			RefreshActionList();
			lvDetails.Items.Clear();
		}

		private void saveToolStripMenuItem_Click(object sender, EventArgs e)
		{
			if (m_ActionList.Count > 0)
			{
				DialogResult result = saveFileDialog.ShowDialog();
				if (result == System.Windows.Forms.DialogResult.OK)
					m_ActionList.Save(saveFileDialog.FileName);
			}
		}

		private void loadToolStripMenuItem_Click(object sender, EventArgs e)
		{
			DialogResult result = openFileDialog.ShowDialog();
			if (result == System.Windows.Forms.DialogResult.OK)
			{
				if (m_ActionList.Count > 0) result = MessageBox.Show("Do you want to clear the current Build Order before loading the file ?", "Clear Build Order", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question);
				if (result == System.Windows.Forms.DialogResult.Cancel) return;
				if (result == System.Windows.Forms.DialogResult.Yes) m_ActionList.Clear();
				m_ActionList.Load(openFileDialog.FileName);
				Run();
			}
		}

		private void lvActionList_ItemDrag(object sender, ItemDragEventArgs e)
		{
			lvActionList.DoDragDrop(lvActionList.SelectedItems, DragDropEffects.Move);
		}

		private void lvActionList_DragEnter(object sender, DragEventArgs e)
		{
			for (int i = 0; i < e.Data.GetFormats().Length; i++)
			{
				if (e.Data.GetFormats()[i] == "System.Windows.Forms.ListView+SelectedListViewItemCollection")
				{
					e.Effect = DragDropEffects.Move;
				}
			}
		}

		private void lvActionList_DragDrop(object sender, DragEventArgs e)
		{
			if (lvActionList.SelectedItems.Count == 0) return;
			Point p = lvActionList.PointToClient(new Point(e.X, e.Y));
			ListViewItem dragToItem = lvActionList.GetItemAt(p.X, p.Y);
			if (dragToItem == null) return;
			int dragIndex = dragToItem.Index;
			ListViewItem[] sel = new ListViewItem[lvActionList.SelectedItems.Count];
			IAction[] act = new IAction[sel.Length];
			for (int c = 0; c < lvActionList.SelectedItems.Count; c++)
			{
				sel[c] = lvActionList.SelectedItems[c];
				act[c] = m_ActionList[lvActionList.SelectedItems[c].Index];
			}

			for (int c = 0; c < lvActionList.SelectedItems.Count; c++)
			{
				ListViewItem dragItem = sel[c];
				int dragItemIndex = dragItem.Index;
				int itemIndex = dragIndex;
				if (itemIndex == dragItem.Index) return;
				if (dragItem.Index < itemIndex) itemIndex++;
				else itemIndex = dragIndex + c;
				ListViewItem insertItem = dragItem.Clone() as ListViewItem;
				lvActionList.Items.Insert(itemIndex, insertItem);
				m_ActionList.Insert(itemIndex, act[c]);
				lvActionList.Items.Remove(dragItem);
				m_ActionList.RemoveAt(itemIndex > dragItemIndex ? dragItemIndex : dragItemIndex + 1);
			}

			m_ActionList.AddMissingRequirements();
			Run();
		}

		private int mintSavedHoverIndex;
		private int mintScrollDirection;
		private ListViewItem mobjHoverItem;

		private void lvActionList_DragOver(object sender, DragEventArgs e)
		{
			Point position =  lvActionList.PointToClient(new Point(e.X, e.Y));
			mobjHoverItem = lvActionList.GetItemAt(position.X, position.Y);
			if (position.Y <= lvActionList.Font.Height / 2)
			{
				mintScrollDirection = 0;
				timerScroll.Enabled = true;
			}
			else if (position.Y >= lvActionList.ClientSize.Height - (lvActionList.Font.Height / 2))
			{
				mintScrollDirection = 1;
				timerScroll.Enabled = true;
			}
			else timerScroll.Enabled = false;
			e.Effect = DragDropEffects.Move;
			position = lvActionList.PointToClient(new Point(e.X, e.Y));
			mobjHoverItem = lvActionList.GetItemAt(position.X, position.Y);
			if (mobjHoverItem == null) return;
			if (mintSavedHoverIndex == mobjHoverItem.Index) return;
			lvActionList.BeginUpdate();
			ClearHighlight(lvActionList);
			mobjHoverItem.BackColor = Color.DarkBlue;
			mobjHoverItem.ForeColor = Color.White;
			lvActionList.EndUpdate();
			mintSavedHoverIndex = mobjHoverItem.Index;
		}

		[DllImport("user32")]
		static extern IntPtr SendMessage(IntPtr Handle, Int32 msg, IntPtr wParam,
		IntPtr lParam);

		private void timerScroll_Tick(object sender, EventArgs e)
		{
			ScrollControl(lvActionList, mintScrollDirection);
		}
		private const int WM_HSCROLL = 0x114;
		private const int WM_VSCROLL = 0x115;
		private void ScrollControl(Control objControl, int intDirection)
		{
			SendMessage(objControl.Handle, WM_VSCROLL, (IntPtr)intDirection, IntPtr.Zero);
		}
		private void ClearHighlight(ListView listView)
		{
			listView.BeginUpdate();
			for (int x = 0; x < listView.Items.Count; x++)
			{
				listView.Items[x].ForeColor = Color.Black;
				listView.Items[x].BackColor = Color.White;
				listView.Items[x].SubItems[0].Text = (x + 1).ToString();
			}
			listView.EndUpdate();
		}

		private void lvDetails_SelectedIndexChanged(object sender, EventArgs e)
		{
			if (lvDetails.SelectedIndices.Count > 0)
			{
				int index = lvDetails.SelectedIndices[0];
				while (index > 0 && string.IsNullOrWhiteSpace(lvDetails.Items[index].SubItems[1].Text))
					index--;
				TimeSpan span;
				if (TimeSpan.TryParse(lvDetails.Items[index].SubItems[1].Text, out span))
					GenerationForm.Run((int)span.TotalSeconds, m_ActionList, pgState);
			}
		}

		private void shortToolStripMenuItem_Click(object sender, EventArgs e)
		{
			GenerationForm.CopyShortDescriptionToClipboard(m_ActionList);
		}

		private void detailledToolStripMenuItem_Click(object sender, EventArgs e)
		{
			GenerationForm.CopyListViewToClipboard(lvDetails);
		}
	}
}
